package gz.metro.datasync.service;

import gz.metro.datasync.entity.model.SyncLogModel;
import gz.metro.datasync.iservice.ISyncService;
import gz.metro.datasync.mapper.SyncLogMapper;
import gz.metro.datasync.utils.JdbcHelper;
import gz.metro.datasync.utils.Result;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author Feily
 * @功能描述 全量同步数据服务基础类
 * @创建时间 2017-12-13
 */
@Slf4j
@Service
@PropertySource(value= "classpath:datasync.properties")
public abstract class SyncService  implements ISyncService {

	@Autowired
	private JdbcHelper jdbcHelper;

	@Autowired
	private SyncLogMapper syncLogMapper;

	/**
	 * 删除同步数据临时表SQL
	 */
	protected String deleteSyncDataSql;
	/**
	 * 根据同步数据保存目标数据SQL
	 */
	protected String saveTargetDataSql;
	/**
	 * 根据同步数据修改目标数据SQL
	 */
	protected String updateTargetDataSql;
	/**
	 * 根据同步数据删除目标表中的已删除数据SQL
	 */
	protected String deleteTargetDataSql;
	/**
	 * 租户编码
	 */
	@Value("${uuap.tenantCode}")
	protected String tenantCode;
	/**
	 * 获取最后更新时间SQL
	 */
	protected String getLastSyncTimeSql;
	/**
	 * 是否全量更新
	 */
	protected boolean isFullSync;
	/**
	 * 同步类型
	 */
	protected String syncType;

	//是否物理删除
	@Value("${sync.physicallyDelete:false}")
	protected boolean physicallyDelete;

	//是否使用自定义sql语句
	@Value("${sync.useCustomSql}")
	protected boolean useCustomSql;

	//是否使用自定义sql语句
	@Value("${sync.syncTargetData:true}")
	protected boolean syncTargetData;

	//获取最后更新时间sql模板
	@Value("${sqlTmpl.getLastUpdateTime}")
	protected String getLastUpdateTimeSqlTmpl;

	//保存目标表数据sql模板
	@Value("${sqlTmpl.saveTargetData}")
	protected String saveTargetDataSqlTmpl;

	//修改目标表数据sql模板
	@Value("${sqlTmpl.updateTargetData}")
	protected String updateTargetDataSqlTmpl;

	//删除目标表中的已删除数据（主表使用）
	@Value("${sqlTmpl.deleteTargetData}")
	protected String deleteTargetDataSqlTmpl;

	//删除目标表数据sql模板（关联表使用）
	@Value("${sqlTmpl.fullDeleteTargetData}")
	protected String fullDeleteTargetDataSqlTmpl;

	//全量保存目标表数据sql模板（关联表使用）
	@Value("${sqlTmpl.fullSaveTargetData}")
	protected String fullSaveTargetDataqlTmpl;

	//同步列配置文档路径
	protected String syncConfigDocPath;

	//同步列配置文档对象
	protected Document syncConfigDoc;

	public String getTenantCode() {
		return tenantCode;
	}

	public void setTenantCode(String tenantCode) {
		this.tenantCode = tenantCode;
	}

	public void setFullSync(boolean fullSync) {
		isFullSync = fullSync;
	}

	public SyncService (){
		System.out.println("init");
	}

	@Override
	public Result syncData() {
		log.info("syncData");

		Result result = new Result();
		String message = "成功同步数据{0}条";

		//创建日志对象，记录开始时间，设置同步状态
		SyncLogModel syncLogModel = new SyncLogModel();
		syncLogModel.setBeginTime(new Date());
		syncLogModel.setIsSuccess(1);

		try {
			//获取同步数据信息列表
			List<Object> syncData = this.getSyncDataList();
			if(syncData == null || syncData.size() == 0){
				message = StringUtils.format(message, 0);
				log.error(message);
				result.setMessage(message);
				return result;
			}
			//删除历史同步数据
			this.deleteSyncData();

			//保存同步数据
			this.saveSyncData(syncData);

			//根据同步数据保存或修改目标数据
			if(syncTargetData){
				this.syncTargetData();
			}

			//设置同步数量
			syncLogModel.setSyncCount(syncData.size());

			message = StringUtils.format(message, syncData.size());
			log.error(message);
			result.setMessage(message);
			return result;
		} catch (Exception ex) {
			log.error("同步数据出错", ex);

			//设置同步状态，记录错误信息
			syncLogModel.setIsSuccess(0);
			syncLogModel.setError(ex.getMessage());

			result.setSuccess(false);
			result.setMessage(ex.getMessage());
			return result;
		} finally {
			//新增同步日志
			syncLogModel.setEndTime(new Date());
			syncLogModel.setSyncType(this.syncType);
			syncLogMapper.insert(syncLogModel);
		}
	}

	@Override
	public List<Object> getSyncDataList() {
		// 获取最后更新时间
		Date lastSyncTime;
		if(isFullSync){
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
			try {
				lastSyncTime = simpleDateFormat.parse("1900-01-01");
			} catch (ParseException e) {
				throw new RuntimeException("获取最后更新时间失败【日期转换失败】");
			}
		}else{
			lastSyncTime = this.getLastSyncTime();
		}
		// 获取同步数据信息列表
		List<Object> syncData = this.getSyncDataList(lastSyncTime);
		return syncData;
	}

	/**
	 * 获取最后更新时间
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public Date getLastSyncTime() {
		log.info("getLastSyncTime");

		if(StringUtils.isEmpty(this.getLastSyncTimeSql)){
			throw new RuntimeException("属性【getLastSyncTimeSql】不能为空");
		}

		Date lastSyncTime = jdbcHelper.queryForObject(this.getLastSyncTimeSql, Date.class);
		if(lastSyncTime != null){
			return lastSyncTime;
		}else{
			int year = 10;
			log.info(StringUtils.format("最后更新时间为空，默认使用{0}年前时间", year));

			Date date = new Date();
			date.setYear(date.getYear() - year);
			return date;
		}
	}

	/**
	 * 获取同步数据信息列表
	 * @param lastSyncTime
	 * @return
	 */
	public abstract List<Object> getSyncDataList(Date lastSyncTime);

	@Override
	public Result deleteSyncData() {
		log.info("deleteSyncData");

		Map<String, Object> params = new HashMap<String, Object>();
		params.put("tenantCode", this.tenantCode);

		jdbcHelper.execute(this.deleteSyncDataSql,params);

		return new Result();
	}

	@Override
	public Result saveSyncData(List<Object> syncData) {
		log.info("saveSyncData");

		return new Result();
	}

	@Override
	public Result syncTargetData() {
		log.info("saveOrUpdateTargetData");

		Map<String, Object> params = new HashMap<String, Object>();
		params.put("tenantCode", this.tenantCode);

		// 删除目标表中的已删除数据
		if (this.physicallyDelete) {
			jdbcHelper.execute(this.deleteTargetDataSql,params);
		}

		// 修改目标数据
		jdbcHelper.execute(this.updateTargetDataSql, params);

		// 保存目标数据
		jdbcHelper.execute(this.saveTargetDataSql, params);

		return new Result();
	}

	/**
	 * 获取最后更新时间SQL
	 * @return
	 */
	public String buildGetLastUpdateTimeSql() throws DocumentException {
		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//目标表名
		String targetTable = table.getText();
		//更新时间列配置元素
		String updateTimeColumn = config.elementText("updateTimeColumn");
		//sql模板
		String sql = this.getLastUpdateTimeSqlTmpl;

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#updateTimeColumn#", updateTimeColumn);

		return sql;
	}

	/**
	 * 根据同步数据保存组织数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildSaveTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//列配置元素
		Element columns = config.element("columns");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.saveTargetDataSqlTmpl;
		//目标列
		String targetColumns = "";
		//源列
		String sourceColumns = "";

		//循环读取列配置
		for (Iterator iter = columns.elementIterator("column"); iter.hasNext();) {
			Element ele = (Element) iter.next();
			String sourceName = ele.attribute("sourceColumn").getText();
			String targetName = ele.getText();
			if(StringUtils.isBlank(sourceName) || StringUtils.isBlank(targetName)){
				continue;
			}

			targetColumns += StringUtils.format(", {0}", targetName);
			sourceColumns += StringUtils.format(", {0}", sourceName);

		}

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey)
				.replaceAll("#targetColumns#", targetColumns.substring(1))
				.replaceAll("#sourceColumns#", sourceColumns.substring(1));

		return sql;
	}

	/**
	 * 根据同步数据修改组织数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildUpdateTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//列配置元素
		Element columns = config.element("columns");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.updateTargetDataSqlTmpl;
		//修改列sql
		String updateColumns = "";

		//循环读取配置
		for (Iterator iter = columns.elementIterator("column"); iter.hasNext();) {
			Element ele = (Element) iter.next();
			String sourceName = ele.attribute("sourceColumn").getText();
			String targetName = ele.getText();
			if(StringUtils.isBlank(sourceName) || StringUtils.isBlank(targetName) || targetName.equals(targetKey)){
				continue;
			}

			updateColumns += StringUtils.format(", t.{0} = s.{1}", targetName, sourceName);

		}

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey)
				.replaceAll("#updateColumns#", updateColumns.substring(1));

		return sql;
	}

	/**
	 * 删除目标表中的已删除数据SQL
	 * @return
	 * @throws DocumentException
	 */
	public String buildDeleteTargetDataSql() throws DocumentException {

		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element table = config.element("table");
		//主键配置元素
		Element key = config.element("key");
		//目标表名
		String targetTable = table.getText();
		//源表名
		String sourceTable = table.attributeValue("sourceTable");
		//目标主键
		String targetKey = key.getText();
		//源主键
		String sourceKey = key.attributeValue("sourceColumn");
		//sql模板
		String sql = this.deleteTargetDataSqlTmpl;

		//替换参数
		sql = sql.replaceAll("#targetTable#", targetTable)
				.replaceAll("#sourceTable#", sourceTable)
				.replaceAll("#targetKey#", targetKey)
				.replaceAll("#sourceKey#", sourceKey);

		return sql;
	}

	/**
	 * 获取同步配置文档对象
	 * @return
	 * @throws DocumentException
	 */
	protected Document getSyncConfigDoc() throws DocumentException {
		if (syncConfigDoc == null){
			//获取配置文件资源
			SAXReader read = new SAXReader();
			try {
				syncConfigDoc = read.read(new ClassPathResource(syncConfigDocPath).getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return syncConfigDoc;
	}

	/**
	 * 获取附加数据库版本的Sql内容
	 * @param sqlKey
	 * @return
	 */
	protected String getSqlContent(String sqlKey) throws DocumentException {
		//根元素
		Element rootElement = this.getSyncConfigDoc().getRootElement();
		//表配置元素
		Element config = rootElement.element(syncType);
		//表配置元素
		Element sqls = config.element("sqls");

		String sqlText = "";
		//循环读取配置
		for (Iterator iter = sqls.elementIterator("sql"); iter.hasNext();) {
			Element ele = (Element) iter.next();
			String sourceName = ele.attribute("key").getValue();
			if (sourceName.equals(sqlKey)){
				sqlText = ele.getText();
				break;
			}

		}

		if(StringUtils.isBlank(sqlText)){
			String message = StringUtils.format("参数【sqlKey】的值（{0}）不存在", sqlKey);
			throw new RuntimeException(message);
		}
		return sqlText;
	}

}